文章目录
  1. 1. 某渣的凭印象回顾
  2. 2. 为什么需要Handler
  3. 3. Handler的设计思想/工作原理
  4. 4. 实践

某渣的凭印象回顾

Handler是线程间消息通讯的一种方式. 使用它需要Message, MessageQueue, Looper几个东西的配合. 每个线程都有一个Looper对象, 它里面有个方法loop()会去轮询消息队列MessageQueue中的Message, 对每个Message去调用它目标线程的Handler来处理这个消息. 轮询的操作会调用native方法使用epoll机制来获得Message. 最常见的使用场景就是子线程中做计算, 将结果返回给主线程, 此时子线程就需要使用主线程的Handler来发送消息到主线程的消息队列, 再由主线程的Handler来处理消息.

为什么需要Handler

参考: <<深入理解Android 卷III>>第二章 深入理解Java Binder和MessageQueue

  1. 线程之间通信的需要. 在Android中, 系统做了一个隐含假设, 即用来显示UI的线程应该只用来显示UI包括更新UI, 不应该做过多的事情, 而同时, 其他线程应该做他们该做的事情, 而不应该更新UI. 因此, 如果试图在一个非UI线程中更新UI组件, 会抛出异常(如果你知道某个叫runOnMainThread的黑科技你当然可以用), 而如果在UI线程中做了过多的事情, 会导致UI线程阻塞(ANR). 这就造成一个问题, 我们经常需要根据业务逻辑或计算结果去更新UI, 这怎么办呢? 就需要其他线程和UI线程之间进行通信, 传递需要的信息.
  2. 事件异步处理的需要. 有时候我们会遇到这样一种情况: 我们希望过一段时间去做某件事, 但又不希望线程阻塞去等待这个时间, 这时候我们当然可以通过新建一个线程在一段时间后与主线程通信的方式来实现这个目的, 但这样显然有点小题大做了, 有没有更好的机制能够异步处理消息而不需要额外创建子线程呢? 答案就是Handler.

Handler的设计思想/工作原理

Handler不是一个孤立的类, 它与MessageQueue, Message, Looper几个类共同构成了线程间消息传递的系统.
其中, Message是具体消息的载体, Handler负责生产和消耗Message, MessageQueue负责把Message构成一个有序的队列, Looper负责从MessageQueue中取出Message对象. Handler, MessageQueue, Looper三个类构成了一个Message流动的循环.

实践

对于一般的线程, 我们只需要定义一个MyHandler类, 让它继承Handler类, 实现我们自己的handleMessage方法, 处理对应类型的Message就可以了, 大多数情况下甚至我们只需要在UI线程代码中定义一个继承Handler的匿名内部类并实现其handleMessage方法足矣. Handler中的sendMessage系列方法可以完成将Message入队的操作. obtainMessage可以获取一个空闲的Message对象. 如果你只满足于Handler的基本使用方法, 那知道这些就足够用了.(完)













































很好, 看来你跟我一样不只想要知道怎么用, 还想知道为什么这样用. Android那帮大牛将Handler设计得很巧妙, 机制简单易懂, 封装得非常好, 以至于我们一般情况下只需要知道上面那段话就行了. 不一般的情况呢, 我们从问题入手, 随便抛出几个问题:

  1. Message具体怎么实现的?它可以承载哪些信息?
  2. Handler默认实现是怎样的?它的obtainMessage做了啥?为什么要用obtainMessage, 直接new Message可以吗?sendMessage又做了啥?这两个方法还可以更高效吗?除了handleMessagesendMessage, Hanlder还可以做什么?
  3. MessageQueue是什么时候创建的?它怎么处理具有延迟时间的Message?MessageQueue有容量限制吗?
  4. Looper是什么时候创建的?它怎么处理Message为空的情况?它怎么把消息分发到正确的Handler对象?

以上都是很容易想到的问题, 而要彻底搞懂它们, 免不了要打开Android源码.
我说的”实践”, 就是看Android源码对Handler的实现.
现在, 先让我们抛开那些问题, 从Handler开始, 一步一步看清这背后的真相. 打开Handler源码查看它的定义, 发现它有以下几个成员:

1
2
3
4
final MessageQueue mQueue;
final Looper mLooper;
final Callback mCallback;
final boolean mAsynchronous;

一看到这几个我们心里就有点猜测了, 前两个不就是前面提到的消息队列和消息循环器吗! 不过后两个是什么鬼? 别急, 先看Handler构造方法.

1
2
3
4
5
6
7
8
9
10
public Handler(Callback callback, boolean async) {
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}

原来上面几个成员变量都在构造函数中初始化了. 其中mLooper使用的是Looper.myLooper()的返回结果, 而mQueue直接使用了mLooper.mQueue. 可以猜测MessageQueue实例是由Looper来创建的, Handler持有这个Looper实例的引用, Handler和mLooper共同持有MessageQueue实例的引用. 构造方法传进来的两个参数用来初始化其余两个成员变量.
来看Looper.myLooper(), 代码如下:

1
2
3
4
5
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}

从Looper的静态成员变量sThreadLocal中获取的Looper实例. 那么有get()应该也有set(), 可我们好像没有看到哪里有调用set()? 以及ThreadLocal这个类是个什么东西?
关于ThreadLocal可以看任玉刚的这篇博客了解, 简单来说就是一个可以存储和访问仅对当前线程可见的数据的类.
我们直奔set()方法去看看, 发现了下面这段代码:

1
2
3
4
5
6
7
8
9
10
public static void prepare() {
prepare(true);
}

private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}

原来是prepare()这个方法直接新建了一个Looper对象放在里面, 这样在之后取的话就能取到这个Looper了. 可是我们好像也没有调用prepare()? 事实上, 如果我们没有调用prepare()的话, 就会抛出上面Handler构造方法里面mLooper为空时的那个异常啦! 所以prepare()这个方法是必调的, 而且一定是在Handler所在线程中先于Handler实例创建.
再来看Looper的构造函数, 如下:

1
2
3
4
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}

明白了, 在Looper构造函数中, 新建了MessageQueue, 并且持有了当前线程的引用.
细心的同学可能会问了: “我在Activity里面没有调用prepare方法, 可我用Handler也用得很6啊!”. 这位同学, 我很欣赏你的细心. Activity就是我们的UI线程了, 为什么我们在UI线程里面可以不调用prepare呢? 答案是, 系统已经帮我们调用过了. 有看过Activity启动源码的同学可能会知道, 在Android中Activity的启动是通过ActivityManagerService做到的, 其中在ActivityThread里面, 会调用Looper.prepareMainLooper()为主线程准备一个Looper, 并且调用Looper.loop()开始轮询. 其中prepareMainLooper()大体上就是调用了prepare方法, 只不过加了个锁来保证主线程Looper对象的唯一性.

到此, 我们已经知道了Looper对象从哪里来, 到哪里去, 我们也知道调用loop()方法之后Looper就开始轮询MessageQueue了, 但我们还不知道Looper对象的loop()方法在干嘛. 看看源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static void loop() {

//获得一个 Looper 对象
final Looper me = myLooper();

// 拿到 looper 对应的 mQueue 对象
final MessageQueue queue = me.mQueue;

//死循环监听(如果没有消息变化,他不会工作的) 不断轮询 queue 中的 Message
for (;;) {
// 通过 queue 的 next 方法拿到一个 Message
Message msg = queue.next(); // 可能会阻塞, 具体是调用native方法时可能阻塞
//空判断
if (msg == null)return;
//消息分发
msg.target.dispatchMessage(msg);
//回收操作
msg.recycleUnchecked();
}
}

取到MessageQueue实例, 死循环读取下一个消息, 再调用Message.target.dispatchMessage(msg)将消息分发到target中去处理.
诶? 这个target是啥? 显然, 它是在Message中设置好的东西, 让我们看看Handler的sendMessage源码. Handler中有许多发送消息的方法, 但追本溯源会到达下面这个方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Handler target;
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
// 使用默认的 handler 构造方法时,mAsynchronous 为 false。
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}

有意思, 发送消息时首先检验MessageQueue是不是为空, 不空的话就执行入队操作, 在入队的方法中让Message对象持有当前Handler的引用, 然后调用MessageQueue的入队方法. 有兴趣的同学可以再深入看一下MessageQueue的入队方法是怎么实现的, 里面会涉及到更加具体的所使用的数据结构, 由于它不影响咱们理解Handler机制, 这里就暂不展开了.
再看dispatchMessage方法:

1
2
3
4
5
6
7
8
9
10
11
12
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}

一切都连起来了. 在这里, 如果你的Message对象中, callback为空, 那么就会执行到Handler成员mCallbackhandleMessage方法或Handler的handleMessage方法, 否则会直接执行handleCallback. 我们去看一下handleCallbackMessage的源码, 就会发现callback是一个Runnable对象, 而handleCallback中直接调用了这个对象的run()方法. 所以我们可以得出结论, 如果设置了Message的callback, 那handleMessage就不起作用了, 转而用callback来处理这个消息. 按Android那帮天才的习惯, Handler里应该有对应的API来供开发者调用. 果不其然, 可以找到post等相关方法.

至此, Message是怎么进入和退出MessageQueue, 又是怎么被Handler处理的, 这个过程我们都已经清楚了. 尝试回答一下本节开头时提出的问题:

  1. Message具体怎么实现的?它可以承载哪些信息?
    A: 看源码.
  2. Handler默认实现是怎样的?它的obtainMessage做了啥?为什么要用obtainMessage, 直接new Message可以吗?sendMessage又做了啥?这两个方法还可以更高效吗?除了handleMessagesendMessage, Hanlder还可以做什么?
    A: 看源码.
  3. MessageQueue是什么时候创建的?它怎么处理具有延迟时间的Message?MessageQueue有容量限制吗?
    A: 看源码, 看我上面的分析.
  4. Looper是什么时候创建的?它怎么处理Message为空的情况?它怎么把消息分发到正确的Handler对象?
    A: 看我上面的分析.

如果你有种上当的感觉的话, 你的感觉是对的, 我上面这一段分析基本上没有回答这几个问题. 其实看到这里你应该也有所感觉, 重要的是这种设计的思路, 厘清了思路之后, 具体能做什么/怎么做, 就是可以自己把控的了.

然鹅, 这里我正正经经回答一下上面那些问题, 免得搜索那些问题进来的同学们失望:

  1. Message具体怎么实现的?它可以承载哪些信息?
    A: android.os.Message是一个final类, 它实现了Parcelable接口, 通过一个int型的flags来标记状态, 状态包括FLAG_IN_USE, FLAG_ASYNCHRONOUS, FLAGS_TO_CLEAR_ON_COPY_FROM三种. Message内主要的成员变量有:
    • 公共成员变量what/arg1/arg2/obj/replyTo/sendingUid, 前三个是整型, 后三个分别是Object, Message, int型. 我们可以通过obtain方法的重载来设置前四个变量.
    • 包可见成员变量flags(及其三个标志状态)/when/data/target/callback/next, 类型分别是int, long, Bundle, Handler, Runnable, Message. 其中除了next以外都有对应的设置方法.
    • 私有变量sPoolSync/sPool/sPoolSize/MAX_POOL_SIZE/gCheckRecycle, 类型分别是Object, Message, int, final int, boolean. 前四个的作用是在消息池非空时同步地从消息池(单链表, sPool指向表头)中摘出表头节点作为obtain()的返回值(所有重载的obtain方法都通过该无参方法获得Message实例), 或者在recycleUnchecked()时将当前消息插入到表头. 表的最大容量是50. 最后一个是在API 21加入的标记, 标志是否要检查回收状态. 使用中的Message被回收时如果该标志为true则抛出IllegalStateException异常.
      总结来说, Message实现了一个最大容量为50的单链表结构, 其类静态变量sPool指向表头, 其实现了Parcelable接口, 可以承载包括Bundle在内的复杂数据.
  2. Handler默认实现是怎样的?它的obtainMessage做了啥?为什么要用obtainMessage, 直接new Message可以吗?sendMessage又做了啥?这两个方法还可以更高效吗?除了handleMessagesendMessage, Hanlder还可以做什么?
    A: android.os.Handler是一个专用于继承实现的类, 它的handleMessage是一个空方法. 它内部定义了一个Callback接口, 含有boolean handleMessage(Message msg)方法, 成员变量mCallback就是这个接口类型, 在Handler的dispatchMessage中拥有第二优先权, 仅次于Message本身的Callback. 继承Handler类容易造成内存泄漏, 这一点需要格外注意. obtainMessage代理了Message类的静态多态方法obtain. 由于Message没有显式定义构造函数, 直接new Message显然是可以的, obtain()就是通过这个构造函数初始化消息池. 但这样做无法循环利用消息池, 造成内存浪费, 因此obtainMessage是更好地获取Message实例的方法. sendMessage做了什么见上文的相关段落, 由于这个是系统调用机制, 效率上我没有想到更好的实现方式. sendMessageAtTime等方法允许子类重写. 除了handleMessagesendMessage, Handler中还定义了一些实用的方法和类, 例如可以获取到Looper, 可以传入Runnable获取Message实例, 可以dump信息, 实现了一个阻塞的final class BlockingRunnable用于内部的runWithScissors. 实现了一个final class MessengerImpl用于在发送Message之前设置Message的Uid为Binder的Uid, 并让包可见成员变量mMessenger持有该类的实例, 但实际上这个成员变量未在别处引用.
  3. MessageQueue是什么时候创建的?它怎么处理具有延迟时间的Message?MessageQueue有容量限制吗?
    A: 上文已经分析了MessageQueue是什么时候创建的(创建Looper时). 在每个消息进入队列(enqueueMessage)的时候, 会根据该消息的时间when将它插入到消息队列链表(这个链表不同于消息池的链表)中, 因此消息队列中的消息都是按照消息时间排序好的, 在取出Message时, 如果没有到该消息的执行时间, 会nativePollOnce等待到执行时间. MessageQueue从代码看没有容量限制.
  4. Looper是什么时候创建的?它怎么处理Message为空的情况?它怎么把消息分发到正确的Handler对象?
    A: 上文已经分析了Looper是什么时候创建的(Looper.prepare()调用时). 它怎么处理Message为空的情况? 答案是退出循环, 因为Message为空只可能出现在MessageQueue已经没有内容时. 它怎么分发到正确的Handler对象, 这可以看前文分析, 答案是在sendMessage调用enqueueMessage时, Handler默认会把每个Messagetarget都设置为它自己, 循环处理时直接调用msg.target.dispatchMessage(msg)来分发. 循环时Looper会保证始终在同一线程上处理消息队列, 如果线程发生变化, Looper会记录一个Log.wtf()日志(注释说是What a Terrible Failure, 但我怎么看都觉得是What The Fxxk). Looper每处理完一个Message都会调用它的recycleUnchecked()让它进入消息池.
文章目录
  1. 1. 某渣的凭印象回顾
  2. 2. 为什么需要Handler
  3. 3. Handler的设计思想/工作原理
  4. 4. 实践